{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_model(params):\n",
    "    # 定义网络结构 和 损失 以及 返回值\n",
    "    pass\n",
    "\n",
    "\n",
    "def  model_fn_builder(params):\n",
    "    # 该方法实际 创建 estimator的model_fn\n",
    "    # 可以 有其他操作\n",
    "    def model_fn(features, labels, mode, params,config): #estimator需要的model_fn 参数固定\n",
    "    '''\n",
    "    features: from input_fn的返回  切记返回的顺序\n",
    "    labels： from input_fn 的返回  切记返回的顺序\n",
    "    mode: tf.estimator.ModeKeys实例的一种\n",
    "    params: 在初始化estimator时 传入的参数列表，dict形式,或者直接使用self.params也可以\n",
    "    config:初始化estimator时 的 Runconfig\n",
    "    \n",
    "    '''\n",
    "        create_model(params)\n",
    "        if mode==tf.estimator.ModeKeys.PREDICT: # 执行预测\n",
    "            #...\n",
    "        elif mode==tf.estimator.ModeKeys.EVAL: #评估\n",
    "            #...\n",
    "        elif mode=tf.estimator.ModeKeys.TRAIN: # 训练\n",
    "            #...\n",
    "        \n",
    "        #......其它操作\n",
    "        \n",
    "        # 最后返回\n",
    "        return tf.estimator.EstimatorSpec(......)\n",
    "    return model_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def input_fn_builder(params):\n",
    "    '''\n",
    "    创建 输入函数闭包\n",
    "    '''\n",
    "    \n",
    "    # 可以执行其它操作\n",
    "    \n",
    "    def input_fn(......):\n",
    "        # 具体操作......\n",
    "        return features,labels # 返回的 顺序要和 model_fn一致 或者 dataset元素 格式为（features,label）元组 也可以\n",
    "                               # 此时，Dataset 包含 (features_dict, labels) 对。这是 train 和 evaluate 方法的预期格式，因此 input_fn 会返回相应的数据集。\n",
    "                               # 使用 predict 方法时，可以/应该忽略 labels\n",
    "    return input_fn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def serving_input_receiver_fn():\n",
    "    '''\n",
    "    定义模型导出后，serving的输入值\n",
    "    '''\n",
    "    #.......各种数据转换\n",
    "    # 在此处 多说一些 关于 batch_features以及 receiver_tensor\n",
    "    # 1. 首先 这两个 参数，相互之间 并没有 直接 的 关系（切记，没有直接关系，说明还是 有间接关系的）\n",
    "    # 2. batch_features这个参数的格式必须 满足 model_fn中features参数格式\n",
    "    # 2.1 关于值的格式，首先他必须是 tensor或者sparseTensor 或者 字典格式（value必须是tensor/sparsetensor）,然后features被传给model\n",
    "    # 2.2 如果 features不是字典，则 该方法会自动将其封装为dict(视为一个样本)，并使用‘feature’作为key\n",
    "    # 2.3 总结：model必须接受一个形如{'feature':tensor}的字典作为入参\n",
    "    # 3.receiver_tensor 这个参数 是用来接收 请求 的 参数，改参数 一般可以 用一个 placeholder代替，后续经过各种变化，\n",
    "    # 将receiver_tensor的值 转换为model_fn中features格式\n",
    "    # 3.1 必须是 tensor或者sparseTensor 或者 字典格式（value必须是tensor/sparsetensor）\n",
    "    return tf.estimator.export.ServingInputReceiver(batch_features,receiver_tensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 初始化 estimator\n",
    "estimator = tf.estimator.Estimator(\n",
    "    model_fn=model_fn_builder,\n",
    "    model_dir=PATH, # path 所有 数据 都保存 在 该目录：包括 checkpoint,log等\n",
    "    params={\"batch_size\": BATCH_SIZE}) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# train model\n",
    "# train(    input_fn,    hooks=None,    steps=None,    max_steps=None,    saving_listeners=None)\n",
    "estimator.train(input_fn=input_fn_builder)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# evaluate model\n",
    "# evaluate(  input_fn,    steps=None,    hooks=None,    checkpoint_path=None,    name=None)\n",
    "estimator.evaluate(input_fn=input_fn_builder)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# predict \n",
    "# predict(    input_fn,    predict_keys=None,    hooks=None,    checkpoint_path=None,    yield_single_examples=True)\n",
    "estimator.predict(...)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# export model for serving\n",
    "#export_savedmodel(    export_dir_base,    serving_input_receiver_fn,    assets_extra=None,    as_text=False,\n",
    "#    checkpoint_path=None,\n",
    "#     strip_default_attrs=False\n",
    "# )\n",
    "estimator.export_savedmodel('export_base',serving_input_receiver_fn=serving_input_receiver_fn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# serving : 2 中方法：\n",
    "#   1. tensorflow serving\n",
    "#   2. tornado/flask  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. tensorflow serving \n",
    "# 最好的 方式 是 使用 docker,详情请请参照：\n",
    "# https://www.tensorflow.org/tfx/serving/docker\n",
    "# https://www.tensorflow.org/tfx/serving/api_rest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2. 使用 tornado/flask\n",
    "# steps：\n",
    "# 1. load model\n",
    "   predictor = tf.contrib.predictor.from_saved_model(model_path) # model_path必须指定具体的版本号\n",
    "# 2. predict\n",
    "   predict_result=predictor(input_params) # input_params 格式必须 符合 serving_input_receiver_fn中入参\n",
    "#     predict_result 格式和 model_fn中返回格式一致\n",
    "# 3. using tornado\n",
    "class b_vxHandler(tornado.web.RequestHandler): \n",
    "\n",
    "    def post(self, version):\n",
    "      \n",
    "        try:\n",
    "           #..... 接收参数并调用model\n",
    "        except BaseException as err:\n",
    "            self.finish(....)\n",
    "\n",
    "\n",
    "application = tornado.web.Application([\n",
    "    (r\"/b/(?P<version>v\\d+)\", b_vxHandler),\n",
    "])\n",
    "\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # tornado.options.parse_command_line()\n",
    "    application.listen(options.port)\n",
    "    tornado.ioloop.IOLoop.instance().start()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py3_NER",
   "language": "python",
   "name": "ner"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
